package com.atguigu.tingshu.user.service.impl;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import com.atguigu.tingshu.album.AlbumFeignClient;
import com.atguigu.tingshu.common.cache.GuiGuCache;
import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.rabbit.constant.MqConst;
import com.atguigu.tingshu.common.rabbit.service.RabbitService;
import com.atguigu.tingshu.model.user.UserInfo;
import com.atguigu.tingshu.model.user.UserPaidAlbum;
import com.atguigu.tingshu.model.user.UserPaidTrack;
import com.atguigu.tingshu.user.mapper.*;
import com.atguigu.tingshu.user.pattern.DeliveryStrategy;
import com.atguigu.tingshu.user.pattern.factory.DeliveryStrategyFactory;
import com.atguigu.tingshu.user.service.UserInfoService;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import com.atguigu.tingshu.vo.user.UserPaidRecordVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
@SuppressWarnings({"all"})
public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo> implements UserInfoService {

    @Autowired
    private UserInfoMapper userInfoMapper;

    @Autowired
    private WxMaService wxMaService;

    @Autowired
    private RedisTemplate redisTemplate;


    @Autowired
    private RabbitService rabbitService;

    @Autowired
    private UserPaidAlbumMapper userPaidAlbumMapper;

    @Autowired
    private UserPaidTrackMapper userPaidTrackMapper;

    @Autowired
    private AlbumFeignClient albumFeignClient;

    @Autowired
    private UserVipServiceMapper userVipServiceMapper;

    @Autowired
    private VipServiceConfigMapper vipServiceConfigMapper;


    /**
     * 微信一键登录
     *
     * @param code 小程序集成微信基于当前微信用户生成临时票据code
     * @return {token:"令牌"}
     */
    @Override
    public Map<String, String> wxLogin(String code) {
        try {
            //1.根据小程序提交的临时票据code调用微信接口服务，获取当前微信用户唯一标识openid
            WxMaJscode2SessionResult sessionInfo = wxMaService.getUserService().getSessionInfo(code);
            String wxOpenid = sessionInfo.getOpenid();
            //2.根据openid查询数据库用户表，判断微信账户是否关联用户
            UserInfo userInfo = userInfoMapper.selectOne(
                    new LambdaQueryWrapper<UserInfo>()
                            .eq(UserInfo::getWxOpenId, wxOpenid)
            );

            //3.如果不存在用户记录，说明该微信是首次登录
            if (userInfo == null) {
                //3.1 新增用户记录
                userInfo = new UserInfo();
                userInfo.setWxOpenId(wxOpenid);
                userInfo.setNickname("听友" + IdUtil.nanoId());
                userInfo.setAvatarUrl("http://192.168.200.6:9000/tingshu/2025-07-19/20108a4b-1ad0-4191-8605-dd12d54c8641.png");
                userInfoMapper.insert(userInfo);
                //3.2 TODO 隐式初始化账户记录，用于后续订单消费,采用MQ
                //3.2.1 创建初始化账户MQ消息对象 注意：如果使用的是VO对象，该对象必须得实现序列化接口 Serializable
                Map<String, Object> mqData = new HashMap<>();
                mqData.put("userId", userInfo.getId());
                mqData.put("amount", BigDecimal.valueOf(100));
                mqData.put("orderNo", "ZS" + DateUtil.today().replaceAll("-", "") + IdUtil.getSnowflakeNextId());
                mqData.put("title", "首次注册，赠送体验金活动");
                //3.2.2 发送消息到RabbitMQ交换机
                rabbitService.sendMessage(MqConst.EXCHANGE_USER, MqConst.ROUTING_USER_REGISTER, mqData);
            }

            //4.无论是否首次登录，都需要生成令牌，返回给小程序
            //4.1 生成随机令牌
            String token = IdUtil.randomUUID();
            //4.2 将用户令牌作为Key，用户基本信息UserInfoVo作为Value，保存到Redis中
            String loginKey = RedisConstant.USER_LOGIN_KEY_PREFIX + token;
            UserInfoVo userInfoVo = BeanUtil.copyProperties(userInfo, UserInfoVo.class);
            redisTemplate.opsForValue().set(loginKey, userInfoVo, RedisConstant.USER_LOGIN_KEY_TIMEOUT, TimeUnit.SECONDS);
            //4.3 返回用户令牌
            return Map.of("token", token);
        } catch (WxErrorException e) {
            log.error("微信登录失败：{}", e.getMessage());
            throw new RuntimeException(e);
        }
    }

    /**
     * 查询指定用户基本信息
     *
     * @return
     */
    @Override
    @GuiGuCache(prefix = "user:info:")
    public UserInfoVo getUserInfo(Long userId) {
        UserInfo userInfo = userInfoMapper.selectById(userId);
        return BeanUtil.copyProperties(userInfo, UserInfoVo.class);
    }

    /**
     * 更新指定用户基本信息 只允许修改集成跟头像
     *
     * @param userInfoVo
     * @param userId
     */
    @Override
    public void updateUser(UserInfoVo userInfoVo, Long userId) {
        UserInfo userInfo = new UserInfo();
        userInfo.setId(userId);
        userInfo.setAvatarUrl(userInfoVo.getAvatarUrl());
        userInfo.setNickname(userInfoVo.getNickname());
        userInfoMapper.updateById(userInfo);
    }

    /**
     * 获取每一页声音列表（提交声音ID集合）对应购买状态
     *
     * @param userId                       用户ID
     * @param albumId                      专辑ID
     * @param needCheckPayStateTrackIdList 需要检查购买状态的声音ID集合
     * @return {声音ID:1(已购),声音ID:0(未购)}
     */
    @Override
    public Map<Long, Integer> userIsPaidTrack(Long userId, Long albumId, List<Long> needCheckPayStateTrackIdList) {
        Map<Long, Integer> payStatusMap = new HashMap<>();
        //1.根据用户ID+专辑ID查询已购专辑记录，如果存在专辑购买记录，将提交声音ID购买状态全部设置为1，返回
        Long count = userPaidAlbumMapper.selectCount(
                new LambdaQueryWrapper<UserPaidAlbum>()
                        .eq(UserPaidAlbum::getUserId, userId)
                        .eq(UserPaidAlbum::getAlbumId, albumId)
        );
        if (count > 0) {
            for (Long trackId : needCheckPayStateTrackIdList) {
                payStatusMap.put(trackId, 1);
            }
            return payStatusMap;
        }

        //2.如果未购买专辑，根据用户ID+专辑ID查询已购声音记录
        List<UserPaidTrack> userPaidTrackList = userPaidTrackMapper.selectList(
                new LambdaQueryWrapper<UserPaidTrack>()
                        .eq(UserPaidTrack::getUserId, userId)
                        .eq(UserPaidTrack::getAlbumId, albumId)
                        .select(UserPaidTrack::getTrackId)
        );

        //2.1 如果不存在购买声音记录，将提交声音ID购买状态全部设置为0，返回
        if (CollUtil.isEmpty(userPaidTrackList)) {
            for (Long trackID : needCheckPayStateTrackIdList) {
                payStatusMap.put(trackID, 0);
            }
            return payStatusMap;
        }

        //2.2 如果存在购买声音记录，将提交声音ID已购设置1，未购买设置为0返回
        //2.2.1 获取已购声音ID集合
        List<Long> userPaidTrackIdList =
                userPaidTrackList.stream().map(UserPaidTrack::getTrackId).collect(Collectors.toList());
        //2.2.2 判断哪些是已购设置为1，哪些是未购买设置为0
        for (Long trackId : needCheckPayStateTrackIdList) {
            if (userPaidTrackIdList.contains(trackId)) {
                //已购买
                payStatusMap.put(trackId, 1);
            } else {
                //未购买
                payStatusMap.put(trackId, 0);
            }
        }
        return payStatusMap;
    }

    /**
     * 判断指定用户是否购买过指定专辑
     *
     * @param userId
     * @param albumId
     * @return
     */
    @Override
    public boolean isPaidAlbum(Long userId, Long albumId) {
        Long count = userPaidAlbumMapper.selectCount(
                new LambdaQueryWrapper<UserPaidAlbum>()
                        .eq(UserPaidAlbum::getUserId, userId)
                        .eq(UserPaidAlbum::getAlbumId, albumId)
        );
        return count > 0;
    }

    /**
     * 查询指定用户指定专辑下已购声音ID列表
     *
     * @param userId
     * @param albumId
     * @return
     */
    @Override
    public List<Long> findUserPaidTrackList(Long userId, Long albumId) {
        List<UserPaidTrack> userPaidTrackList = userPaidTrackMapper.selectList(
                new LambdaQueryWrapper<UserPaidTrack>()
                        .eq(UserPaidTrack::getUserId, userId)
                        .eq(UserPaidTrack::getAlbumId, albumId)
                        .select(UserPaidTrack::getTrackId)
        );
        if (CollUtil.isNotEmpty(userPaidTrackList)) {
            List<Long> userPaidTrackIdList = userPaidTrackList.stream()
                    .map(UserPaidTrack::getTrackId)
                    .collect(Collectors.toList());
            return userPaidTrackIdList;
        }
        return null;
    }

    @Autowired
    private DeliveryStrategyFactory deliveryStrategyFactory;

    /**
     * 用户支付成功后，权益方法，虚拟物品（VIP会员、专辑、声音）发货
     *
     * @param userPaidRecordVo
     */
    @Override
    public void savePaidRecord(UserPaidRecordVo userPaidRecordVo) {
        //项目类型: 1001-专辑 1002-声音 1003-vip会员
        String itemType = userPaidRecordVo.getItemType();

        //1.从策略工厂获取具体策略实现类对象
        DeliveryStrategy strategy = deliveryStrategyFactory.getDeliveryStrategy(itemType);

        //2.调用策略实现类对象，进行发货方法调用
        strategy.delivery(userPaidRecordVo);

/*
        //1.处理购买项目类型为专辑
        if (SystemConstant.ORDER_ITEM_TYPE_ALBUM.equals(itemType)) {
            //1.1 根据订单编号查询是否存在购买记录，如果存在则忽略
            Long count = userPaidAlbumMapper.selectCount(
                    new LambdaQueryWrapper<UserPaidAlbum>()
                            .eq(UserPaidAlbum::getOrderNo, userPaidRecordVo.getOrderNo())
                            .select(UserPaidAlbum::getId)
            );
            if (count > 0) {
                return;
            }
            //1.2 新增专辑购买记录
            UserPaidAlbum userPaidAlbum = new UserPaidAlbum();
            userPaidAlbum.setOrderNo(userPaidRecordVo.getOrderNo());
            userPaidAlbum.setUserId(userPaidRecordVo.getUserId());
            userPaidAlbum.setAlbumId(userPaidRecordVo.getItemIdList().get(0));
            userPaidAlbumMapper.insert(userPaidAlbum);
        } else if (SystemConstant.ORDER_ITEM_TYPE_TRACK.equals(itemType)) {
            //2. 处理购买项目类型为声音
            //2.1 根据订单编号查询是否存在购买记录，如果存在则忽略
            Long count = userPaidTrackMapper.selectCount(
                    new LambdaQueryWrapper<UserPaidTrack>()
                            .eq(UserPaidTrack::getOrderNo, userPaidRecordVo.getOrderNo())
                            .select(UserPaidTrack::getId)
            );
            if (count > 0) {
                return;
            }
            //2.2 新增声音购买记录
            //远程调用专辑服务获取声音信息，得到所属专辑ID
            TrackInfo trackInfo = albumFeignClient.getTrackInfo(userPaidRecordVo.getItemIdList().get(0)).getData();
            Long albumId = trackInfo.getAlbumId();
            userPaidRecordVo.getItemIdList().forEach(trackId -> {
                UserPaidTrack userPaidTrack = new UserPaidTrack();
                userPaidTrack.setOrderNo(userPaidRecordVo.getOrderNo());
                userPaidTrack.setUserId(userPaidRecordVo.getUserId());
                userPaidTrack.setAlbumId(albumId);
                userPaidTrack.setTrackId(trackId);
                userPaidTrackMapper.insert(userPaidTrack);
            });
        } else if (SystemConstant.ORDER_ITEM_TYPE_VIP.equals(itemType)) {
            //3.TODO 处理购买项目类型为VIP会员
            //3.1 根据订单编号查询是否存在购买记录，如果存在则忽略
            Long cout = userVipServiceMapper.selectCount(
                    new LambdaQueryWrapper<UserVipService>()
                            .eq(UserVipService::getOrderNo, userPaidRecordVo.getOrderNo())
                            .select(UserVipService::getId)
            );
            if (cout > 0) {
                return;
            }
            //3.2 新增会员购买记录
            UserVipService userVipService = new UserVipService();
            userVipService.setOrderNo(userPaidRecordVo.getOrderNo());
            userVipService.setUserId(userPaidRecordVo.getUserId());
            //3.2.1 获取当前用户身份，判断是否为VIP
            Boolean isVIP = false;
            UserInfoVo userInfoVo = getUserInfo(userPaidRecordVo.getUserId());
            if (userInfoVo.getIsVip().intValue() == 1 && userInfoVo.getVipExpireTime().after(new Date())) {
                isVIP = true;
            }
            //3.2.2 获取用户购买套餐信息，得到套餐服务月
            Long vipID = userPaidRecordVo.getItemIdList().get(0);
            VipServiceConfig vipServiceConfig = vipServiceConfigMapper.selectById(vipID);
            Integer serviceMonth = vipServiceConfig.getServiceMonth();
            //3.2.3 本次会员生效时间
            if(!isVIP){
                // 如果是普通用户，生效时间为当前时间
                userVipService.setStartTime(new Date());
                //3.2.4 本次会员到期时间
                userVipService.setExpireTime(DateUtil.offsetMonth(new Date(), serviceMonth));
            }else{
                // 如果是VIP用户
                // 生效时间=现有会员到期时间+1天
                userVipService.setStartTime(DateUtil.offsetDay(userInfoVo.getVipExpireTime(), 1));
                userVipService.setExpireTime(DateUtil.offsetMonth(userVipService.getStartTime(), serviceMonth));
            }
            userVipServiceMapper.insert(userVipService);

            //3.3 更新用户表会员标识以及会员到期时间
            UserInfo userInfo = new UserInfo();
            userInfo.setId(userPaidRecordVo.getUserId());
            userInfo.setIsVip(1);
            userInfo.setVipExpireTime(userVipService.getExpireTime());
            userInfoMapper.updateById(userInfo);
        }*/
    }

    /**
     * 清理过期会员
     * @param date
     */
    @Override
    public void updateVipExpireStatus(Date date) {
        //1.找出已过期会员用户列表
        List<UserInfo> userInfoList = userInfoMapper.selectList(
                new LambdaQueryWrapper<UserInfo>()
                        .eq(UserInfo::getIsVip, 1)
                        .lt(UserInfo::getVipExpireTime, date)
                        .select(UserInfo::getId)
        );
        //2.更新会员标识
        if(CollUtil.isNotEmpty(userInfoList)){
            for (UserInfo userInfo : userInfoList) {
                userInfo.setIsVip(0);
                userInfoMapper.updateById(userInfo);
            }
        }
    }
}
